﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Xml.Serialization;
using vJine.Core.IO.Bin;
using vJine.Core.ORM;

namespace vJine.Lua.GUI {
    public class NodeConfig : List<NodeConfig>, INotifyPropertyChanged {

        public NodeConfig() {
        }

        public NodeConfig(bool is_batch) {
            this.is_batch = is_batch;
        }

        public NodeConfig clone() {
            return new NodeConfig(this.is_batch) {
                uri = this.uri
            };
        }

        public string path { get; set; }
        
        public string uri { get; set; }

        internal string value_path { get; set; }
        internal string cmd_exec { get; set; }
        internal string cmd_changed { get; set; }
        internal string cmd_dbclick { get; set; }

        /// <summary>
        /// 禁止节点运行
        /// </summary>
        bool _disable = false;
        public bool disable {
            get {
                return this._disable;
            }
            set {
                if (value != this._disable) {
                    this._disable = value;
                    this.NotifyPropertyChanged("disable");
                }
            }
        }

        /// <summary>
        /// 暂停节点运行
        /// </summary>
        bool _pause = false;
        public bool pause {
            get {
                return this._pause;
            }
            set {
                if (value != this._pause) {
                    this._pause = value;
                    this.NotifyPropertyChanged("pause");
                }
            }
        }

        string _param = null;
        /// <summary>
        /// 参数名称（命名后在显示在主屏）
        /// </summary>
        public string param {
            get {
                return this._param;
            }
            set {
                if (value != this._param) {
                    this._param = value;
                    this.NotifyPropertyChanged("param");
                }
            }
        }

        string _formula = null;
        /// <summary>
        /// 数据引用（Razor语法）
        /// </summary>
        public string formula {
            get {
                return this._formula;
            }
            set {
                if (value != this._formula) {
                    this._formula = value;
                    this.NotifyPropertyChanged("formula");
                }
            }
        }

        int _times = 1;
        /// <summary>
        /// 运行次数
        /// </summary>
        public int times {
            get {
                return this._times;
            }
            set {
                if (value != this._times) {
                    this._times = value;
                    this.NotifyPropertyChanged("times");
                }
            }
        }

        int _counter = 0;
        /// <summary>
        /// 运行计数
        /// </summary>
        public int counter {
            get {
                return this._counter;
            }
            set {
                if (value != this._counter) {
                    this._counter = value;
                    this.NotifyPropertyChanged("counter");
                }
            }
        }

        int _step = 0;
        /// <summary>
        /// 运行指定次数后暂定
        /// </summary>
        public int step {
            get {
                return this._step;
            }
            set {
                if (value != this._step) {
                    this._step = value;
                    this.NotifyPropertyChanged("step");
                }
            }
        }

        double _delay = 0;
        /// <summary>
        /// 运行延时
        /// </summary>
        public double delay {
            get {
                return this._delay;
            }
            set {
                if (value != this._delay) {
                    this._delay = value;
                    this.NotifyPropertyChanged("delay");
                }
            }
        }

        /// <summary>
        /// 输出日志
        /// </summary>
        bool _log = true;
        public bool log {
            get {
                return this._log;
            }
            set {
                if (value != this._log) {
                    this._log = value;
                    this.NotifyPropertyChanged("log");
                }
            }
        }

        bool _data = false;
        /// <summary>
        /// 保存数据
        /// </summary>
        public bool data {
            get {
                return this._data;
            }
            set {
                if (value != this._data) {
                    this._data = value;
                    this.NotifyPropertyChanged("data");
                }
            }
        }

        bool _show_before = false;
        /// <summary>
        /// 运行前提示
        /// </summary>
        public bool show_before {
            get {
                return this._show_before;
            }
            set {
                if (value != this._show_before) {
                    this._show_before = value;
                    this.NotifyPropertyChanged("show_before");
                }
            }
        }

        bool _show_after = false;
        //运行后提示
        public bool show_after {
            get {
                return this._show_after;
            }
            set {
                if (value != this._show_after) {
                    this._show_after = value;
                    this.NotifyPropertyChanged("show_after");
                }
            }
        }

        /// <summary>
        /// 节点名称（用于程序配置配置）
        /// </summary>
        public string name { get; set; }

        /// <summary>
        /// 节点别名（用于用户界面显示）
        /// </summary>
        public string alias { get; set; }

        /// <summary>
        /// 命令名称(显示于运行按钮)
        /// </summary>
        public string cmd_name { get; set; }

        /// <summary>
        /// 界面切换时默认tab索引(0:输入参数, 1:输出结果, 2:节点选项)
        /// </summary>
        public int tab { get; set; }

        /// <summary>
        /// UI节点不可拖动，TODO:进一步统一
        /// </summary>
        public bool ignore { get; set; }

        /// <summary>
        /// 初始化节点（程序首次运行强制调用直至成功调用一次）
        /// </summary>
        public bool is_init { get; set; }

        /// <summary>
        /// 标记初始化节点是否已成功调用
        /// </summary>
        bool isIntialized = false;

        /// <summary>
        /// 不调用初始化节点
        /// </summary>
        public bool init_ignore { get; set; }

        Object _value;
        /// <summary>
        /// 节点默认值
        /// </summary>
        public Object value {
            get {
                return this._value;
            }
            set {
                if (value != this._value) {
                    this._value = value;
                    this.NotifyPropertyChanged("value");
                }
            }
        }

        /// <summary>
        /// 显示格式
        /// </summary>
        public string format { get; set; }

        /// <summary>
        /// 参数列表
        /// </summary>
        public List<ListItem> list { get; set; }

        /// <summary>
        /// 多选列表
        /// </summary>
        public bool multi { get; set; }

        /// <summary>
        /// 是否是返回值
        /// </summary>
        public bool r { get; set; }

        /// <summary>
        /// 是否保存节点更改值
        /// </summary>
        public bool s { get; set; }

        /// <summary>
        /// 备注
        /// </summary>
        public string remark { get; set; }

        /// <summary>
        /// 节点参数
        /// </summary>
        public NodeConfig configs { get; set; }

        public void set(LuaContext context, string root, bool  set_init = true) {

            if (set_init) {
                this.inits = this.inits ?? new List<NodeConfig>();
                if (this.is_init && !this.inits.Contains(this)) {
                    this.inits.Add(this);
                }
            }

            this.path = root;
            this.uri = 
                string.IsNullOrEmpty(this.uri) ? this.name : this.uri + "." + this.name;

            this.value_path = root + ".value";

            this.cmd_exec = root + ".cmd";
            if (!context.is_function(this.cmd_exec)) {
                this.cmd_exec = null;
            }
            this.cmd_dbclick = root + ".on_dbclick";
            if (!context.is_function(this.cmd_dbclick)) {
                this.cmd_dbclick = null;
            }

            this.set_value(context, this);
            this.cmd_changed = root + ".on_changed";
            if (!context.is_function(this.cmd_changed)) {
                this.cmd_changed = null;
            }

            this.PropertyChanged += (object sender, PropertyChangedEventArgs e) => {
                if (e.PropertyName == "value") {
                    this.set_value(context, this);

                    if (!this.trig_on_changed) {
                        return;
                    }

                    //保存配置值
                    if (this.s && context.is_function("on_save")) {
                        context.exec(
                            "on_save", this.uri, this.value == null ? "" : this.value.ToString()
                            );
                    }

                    try {
                        this.exec(context, this.cmd_changed);
                    } catch (Exception ex) {
                        if (ex == vJine.Core.CoreException.Cancel) {
                            throw ex;
                        }

                        if (ex.InnerException != null) {
                            ex = ex.InnerException;
                        }
                        context.exec("on_error", ex.Message);
                    }
                }
            };

            for (int i = 0, len = this.Count; i < len; i++) {
                NodeConfig config_i = this[i];
                config_i.uri = this.uri;

                config_i.inits = this.inits;
                config_i.set(context, string.Format("{0}.{1}", root, i + 1));
            }

            if (this.configs != null && this.configs.Count > 0) {
                for (int i = 0, len = this.configs.Count; i < len; i++) {
                    NodeConfig config_i = this.configs[i];
                    config_i.uri = this.uri;

                    config_i.inits = this.inits;
                    config_i.set(context, string.Format("{0}.configs.{1}", root, i + 1));
                }

                //TODO:设置上下文报错
                context.get(root + ".configs");
                context.get("__CONFIG_INDEX_HELPER");
                context.nexec("setmetatable", 2);
            }
        }

        public NodeConfig get(string uri) {
            if (this.uri == uri) {
                return this;
            }

            if (this.configs != null) {
                for (int i = 0, len = this.configs.Count; i < len; i++) {
                    NodeConfig config_i = this.configs[i];
                    if (config_i.uri == uri) {
                        return config_i;
                    }
                }
            }

            for (int i = 0, len = this.Count; i < len; i++) {
                NodeConfig config_i = this[i].get(uri);
                if (config_i != null) {
                    return config_i;
                }
            }

            return null;
        }
        /// <summary>
        /// 设置NodeConfig.value的值
        /// </summary>
        /// <param name="luaContext">上下文</param>
        /// <param name="nConfig">节点</param>
        void set_value(LuaContext luaContext, NodeConfig nConfig) {
            if (nConfig.list == null) {
                luaContext.set(nConfig.value_path, this.value);
            } else if (nConfig.multi) {
                List<Object> objs = new List<object>();
                for (int i = 0, len = nConfig.list.Count; i < len; i++) {
                    ListItem list_i = nConfig.list[i];
                    if (list_i.Checked) {
                        objs.Add(list_i.Value);
                    }
                }
                luaContext.set(nConfig.value_path, objs);
            } else {
                for (int i = 0, len = nConfig.list.Count; i < len; i++) {
                    ListItem list_i = nConfig.list[i];
                    if (list_i.Checked) {
                        luaContext.set(nConfig.value_path, list_i.Value);
                    }
                }
            }
        }

        public void set_config(LuaContext luaContext, Dictionary<string, string> Configs) {
            this.set_config(luaContext, Configs, this);
        }

        public void set_config(LuaContext luaContext, Dictionary<string, string> Configs, NodeConfig Config) {
            foreach (KeyValuePair<string, string> kv in Configs) {
                NodeConfig config_i = Config.get(kv.Key);
                if (config_i == null) {
                    continue;
                }

                string stringValue = kv.Value;

                bool trig = config_i.trig_on_changed;
                try {
                    config_i.trig_on_changed = false;
                    double numberValue = 0;
                    if (double.TryParse(stringValue, out numberValue)) {
                        config_i.value = numberValue;
                    } else {
                        config_i.value = stringValue;
                    }
                } finally {
                    config_i.trig_on_changed = trig;
                }
            }
        }

        /// <summary>
        /// 初始化节点集合
        /// </summary>
        internal List<NodeConfig> inits;
        
        /// <summary>
        /// 调用初始化节点
        /// </summary>
        /// <param name="context"></param>
        /// <param name="caller"></param>
        /// <param name="force"></param>
        void init(LuaContext context, NodeConfig caller, bool force = false) {
            if (caller != null && caller.isIntialized) {
                return;
            }
            if (caller != null && caller.init_ignore) {
                return;
            }

            for (int i = 0, len = this.inits.Count; i < len; i++) {
                NodeConfig config_init = this.inits[i];
                if (!config_init.is_init) {
                    continue;
                }

                if (config_init == caller) {
                    continue;
                }
                if (config_init.isIntialized) {
                    if (!force) {
                        continue;
                    }
                }

                config_init.exec(context, config_init.cmd_exec);
                //config_init.isIntialized = true;
            }
        }

        public bool is_runnable {
            get {
                if (!this.disable && this.counter < this.times) {
                    return true;
                } else if (this.is_batch && this.Count > 0) {
                    return true;
                } else if (this.cmd_exec != null || this.cmd_changed != null || this.cmd_dbclick != null) {
                    return false;
                } else if(this.step > 0 && this.counter % this.step == 0) {
                    return true;
                }

                return false;
            }
        }

        void show_log(LuaContext context, string uri, string msg, params object[] args) {
            if (context.is_function("on_log")) {
                context.exec("on_log", string.Format(msg, args));
            }
        }

        void show_warn(LuaContext context, string uri, string msg, params object[] args) {
            if (context.is_function("on_warn")) {
                context.exec("on_warn", string.Format(msg, args));
            }
        }

        /// <summary>
        /// 重试:true,忽略:false,取消:null
        /// </summary>
        /// <param name="context"></param>
        /// <param name="uri"></param>
        /// <param name="msg"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        bool? show_error(LuaContext context, string uri, string msg, params object[] args) {
            if (context.is_function("on_error")) {
               object[] objs = context.exec("on_error", string.Format(msg, args));
               if (objs == null && objs.Length == 0) {
                   return null;
               } else {
                   return (bool?)objs[0];
               }
            }
            return null;
        }

        object exec(LuaContext context, NodeConfig runConfig, string cmd) {
            if (string.IsNullOrEmpty(cmd) || !context.is_function(cmd)) {
                return null;
            }

        RETRY: ;
            lock (runConfig) {
                bool trig = runConfig.trig_on_changed;
                try {
                    runConfig.init(context, runConfig);

                    if (runConfig.log) {
                        this.show_log(context, runConfig.uri, "[调用] " + this.uri);
                    }

                    string root_base = runConfig.path;
                    {
                        root_base = root_base.Substring(0, root_base.LastIndexOf("."));
                        root_base = root_base.Substring(0, root_base.LastIndexOf("."));
                        //TODO:root_base
                        if (cmd != this.cmd_exec) {
                            root_base = root_base.Substring(0, root_base.LastIndexOf("."));
                        }
                    }
                    context.chain(runConfig.path);
                    //(context, value)
                    context.get(root_base); //
                    object[] objs = context.nexec(cmd, 1, runConfig.value);
                    runConfig.isIntialized = true;

                    if (runConfig.configs != null && runConfig.configs.Count > 0) {
                        for (int i = 0, len = runConfig.configs.Count; i < len; i++) {
                            NodeConfig config_i = runConfig.configs[i];
                            if (config_i.r) {
                                config_i.value =
                                    context.getobj(runConfig.path + ".configs." + config_i.name);
                            }
                        }
                    }

                    if (runConfig.delay > 0) {
                        Thread.Sleep((int)(runConfig.delay * 1000));
                    }

                    if (objs != null && objs.Length > 0) {
                        return objs[0];
                    } else {
                        return null;
                    }
                } catch (Exception ex) {
                    if (ex == vJine.Core.CoreException.Cancel) {
                        throw ex;
                    }

                    if (ex.InnerException != null) {
                        ex = ex.InnerException;
                    }

                    bool? r = this.show_error(context, runConfig.uri,
                        "节点:<{0}> 调用失败, {1}", runConfig.uri, ex.Message);

                    if (r == true) {
                        goto RETRY;
                    } else if (r == false) {
                        this.show_warn(context, runConfig.uri, "节点:<{0}>, 调用被忽略 ", runConfig.uri);
                    } else {
                        throw vJine.Core.CoreException.Cancel;
                    }
                    return null;
                } finally {
                    runConfig.trig_on_changed = trig;
                }
            }
        }

        public object exec(LuaContext context, string cmd) {
            this.counter = 0;
            object obj = null;
            while (this.is_runnable) {
                obj = this.exec(context, this, cmd);
                this.counter += 1;
            }

            return obj;
        }

        /// <summary>
        /// 运行节点下的多个节点(如Main)
        /// </summary>
        public bool is_batch { get; private set; }
        public void run(LuaContext context){
            if (this.is_batch) {
                if (this.counter >= this.times) {
                    this.counter = 0;
                }
                //获取默认值
                List<object> defaults = this.get_defaults();
                try {
                    while (this.counter < this.times) {
                        //设置参数值
                        this.set_params(this, defaults, ++this.counter);

                        for (int i = 0, len = this.Count; i < len; i++) {
                            NodeConfig runConfig = this[i];
                            runConfig.exec(context, runConfig.cmd_exec);
                        }

                        if (this.data && context.is_function("on_data")) {
                            context.exec("on_data", this.uri, this);
                        }
                    }
                } catch(Exception ex) {
                    if (ex == vJine.Core.CoreException.Cancel && this.counter > 0) {
                        this.counter -= 1;
                    }
                } finally {
                    //恢复默认值
                    this.set_defaults(defaults);
                }
            } else {
                this.exec(context, this.cmd_exec);
            }
        }

        //获取参数默认值
        List<Object> get_defaults() {
            List<Object> defaults = new List<object>();
            for (int i = 0, len = this.configs.Count; i < len; i++) {
                defaults.Add(this.configs[i].value);
            }

            return defaults;
        }

        void set_defaults(List<object> defaults) {
            for (int i = 0, len = this.configs.Count; i < len; i++) {
                this.configs[i].value = defaults[i];
            }
        }

        void set_params(NodeConfig runConfig, List<object> defaults, int counter) {
            NodeConfig Params = runConfig.configs;
            if (Params == null || Params.Count == 0) {
                return;
            }

            for (int i = 0, len = Params.Count; i < len; i++) {
                NodeConfig param_i = Params[i];
                if (string.IsNullOrEmpty(param_i.formula)) {
                    continue;
                }

                object default_i = defaults[i];
                string default_value = 
                    default_i == null? "NULL" : default_i.ToString();

                string param_value = param_i.formula;
                if (param_value.IndexOf("@counter") >= 0) {
                    param_value = param_value.Replace("@counter", counter.ToString());
                }
                if (param_value.IndexOf("@") >= 0) {
                    param_value = param_value.Replace("@", default_value);
                }

                if (default_i == null) {
                    param_i.value = param_value;
                } else if(default_i.GetType() == typeof(double)) {
                    param_i.value = double.Parse(param_value);
                } else {
                    param_i.value = param_value;
                }
            }
        }

        #region UI
        private bool _IsActive;
        public bool IsActive {
            get { return this._IsActive; }
            set {
                if (value != this._IsActive) {
                    this._IsActive = value;
                    this.NotifyPropertyChanged("IsActive");
                }
            }
        }

        private bool _IsExpanded;
        public bool IsExpanded {
            get { return this._IsExpanded; }
            set {
                if (value != this._IsExpanded) {
                    this._IsExpanded = value;
                    this.NotifyPropertyChanged("IsExpanded");
                }
            }
        }
        #endregion

        #region INotifyPropertyChanged 成员
        bool _trig_on_changed = true;
        internal bool trig_on_changed {
            get {
                return this._trig_on_changed;
            }
            set {
                this._trig_on_changed = value;

                if (this.configs != null && this.configs.Count > 0) {
                    for (int i = 0, len = this.configs.Count; i < len; i++) {
                        this.configs[i].trig_on_changed = value;
                    }
                }
            }
        }

        protected void NotifyPropertyChanged(string Name) {
            if (this.PropertyChanged != null) {
                try {
                    this.PropertyChanged(this, new PropertyChangedEventArgs(Name));
                } catch (Exception ex) {
                    if (ex == vJine.Core.CoreException.Cancel) {
                        return;
                    }
                    throw ex;
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
